// Funktiot jotka suoritetaan onnistuneen API kutsun päätteeksi
var __APICALL_FUNC = {
    "Login/doLogout": function(result) {
        location.reload(true);
    }
};

// Montako API kutsua on menossa
var __API_CALLS = 0;

// Onko modaalinen vahvistusdialogi jo alustettu
var __CONFIRM_INIT = false;

// Modaalisen vahvistusdialogin Confirm painikkeen käsittelijä
var __CONFIRM_FUNC = undefined;

// Onko modaalinen dialogi jo alustettu
var __DIALOG_INIT = false;

// Onko modaalinen latausruutu jo alustettu
var __LOADER_INIT = false;

// Modaalisen syötedialogin vahvistuspainikkeen käsittelijä
var __PROMPT_FUNC = undefined;

// Onko modaalinen syötedialogi jo alustettu
var __PROMPT_INIT = false;

// Onko alinäkymä latautumassa
var __SUBVIEW_LOADING = {};

// Onko näkymä latautumassa    
var __VIEW_LOADING = false;

// Ladatut näkymien JavaScriptit
var __VIEW_SCRIPTS = [];

// FormTools
var FT = SPFormTools;

// Luokka harjoitus hallintaa varten
var ExerciseControl = new function() {
    var _this = this;
        
    // Onko dialogi jo alustettu
    var _dialogInit = false;
    
    // Lista harjoitteista
    var _exercises = null;
    
    // Funktio joka suoritetaan kun harjoitus valitaan
    var _selectFunc = undefined;
    
    // Levytankojen tyypit
    this.barbellTypes = {
        "buffalo_bar":  "Buffalo bar",
        "cambered_bar": "Cambered bar",
        "curl_bar":     "Curl bar",
        "olympic_bar":  "Olympic bar",
        "safety_bar":   "Safety bar",
        "standard_bar": "Standard bar",
        "swiss_bar":    "Swiss bar",
        "thick_bar":    "Thick bar",
        "trap_bar":     "Trap bar"
    };
    
    /**
     * Funktio alustaa harjoituslistan jos sitä ei ole vielä alustettu.
     */
    var _init = function() {
        if (_exercises !== null) {
            return;
        }
        
        _exercises = {};
        
        $.ajax({
            async:      false,
            dataType:   "json",
            type:       "POST",
            url:        "gateway/request/Exercise/getList",
            success:    function(result) {
                _exercises = result["data"];
            }
        });
    }
    
    /**
     * Funktio alustaa harjoituksen valinnan dialogin.
     */
    var _initDialog = function() {
        if (_dialogInit) {
            return;
        }
        
        var $div = $("<div>").
            addClass("modal").
            attr("id","div-exercise-select-dialog");
            
        var $divModal = $("<div>").
            addClass("modal-dialog");
            
        var $divContent = $("<div>").
            addClass("modal-content");
        
        // Otsikko        
        var $divHeader = $("<div>").
            addClass("modal-header");
            
        $(FT.button(null,{
            "class":        "close",
            "data-dismiss": "modal" 
        })).
            prepend(
                $("<span>").
                    addClass("sr-only").
                    text("Close")
            ).
            prepend(
                $("<span>").
                    attr("aria-hidden","true").
                    html("&times;")
            ).            
            appendTo($divHeader);
            
        $divHeader.
            append($("<h4>").text("Select exercise")).
            appendTo($divContent);
        
        // Sisältö
        var $divBody = $("<div>").
            addClass("modal-body");
        
        var $divFormGroup = $("<div>").
            addClass("form-group");
        
        // Harjoituskategoriat        
        var categories  = _this.getExerciseCategories();
        var cat         = Object.keys(categories)[0];
        var arr         = Object.keys(categories[cat])[0].split("|");
        var exercises   = ExerciseControl.getCategoryExercises(arr[0],arr[1]);
        
        var $label = $("<label>").
            attr("for","div-exercise-select-exercisecategory").
            text("Exercise category");
        
        var $categorySelect = $(FT.select(null,categories,{
                "class":    "form-control",
                "id":       "div-exercise-select-exercisecategory"
            })
        );
        
        $divFormGroup.
            clone().
            append($label).
            append($categorySelect).
            appendTo($divBody);
        
        // Harjoitteen valinta    
        var $label = $("<label>").
            attr("for","div-exercise-select-exerciseid").
            text("Exercise");
        
        var $exerciseSelect = $(FT.select(null,exercises,{
                "class":    "form-control",
                "id":       "div-exercise-select-exerciseid"  
            })
        );
        
        $divFormGroup.
            clone().
            append($label).
            append($exerciseSelect).
            appendTo($divBody);
            
        // Levytankotyypin valinta    
        var $label = $("<label>").
            attr("for","div-exercise-select-barbell-type").
            text("Barbell type");
        
        var $barbellSelect = $(FT.select(null,_this.barbellTypes,{
                "class":    "form-control",
                "id":       "div-exercise-select-barbell-type"  
            })
        ).
            prepend(
                $("<option>").
                    attr("value","").
                    text("- Not selected -")
            );
        
        if (arr[0] != "Barbell") {
            $barbellSelect.
                attr("disabled","disabled");
        }
        
        $divFormGroup.
            clone().
            append($label).
            append($barbellSelect).
            appendTo($divBody);
            
        $divBody.
            appendTo($divContent);
        
        // Footer
        $divFooter = $("<div>").
            addClass("modal-footer");
        
        $(FT.button("Select",{
            "class":    "btn btn-default",
            "id":       "div-exercise-select-button-select"
        })).
            appendTo($divFooter);
        
        $divFooter.
            appendTo($divContent);
        
        // Liitä elementit yhteen
        $divModal.
            append($divContent).
            appendTo($div);
            
        $div.
            appendTo($("body"));
            
        $div.show().modal({
            backdrop:   "static",
            keyboard:   false,
            show:       false
        });
        
        // Harjoituskategorian muutos
        $(document).on("change","#div-exercise-select-exercisecategory",function(){
            var arr = $(this).
                val().
                split("|");
            
            if (arr[0] == "Barbell") {
                $barbellSelect.
                    removeAttr("disabled").
                    val("standard_bar");
            } else {
                $barbellSelect.
                    attr("disabled","disabled");
            }
            
            $exerciseSelect.
                html(FT.options(_this.getCategoryExercises(arr[0],arr[1])));
        });
        
        // Harjoituksen valinta
        $(document).on("click","#div-exercise-select-button-select",function(){
            var barbellType = null;
            var val         = $exerciseSelect.val();
            var text        = $exerciseSelect.
                find("option:selected").
                text();
            
            // Onko levytangon tyyppi aktiivinen
            if (!$barbellSelect.is(":disabled")) {
                barbellType = $barbellSelect.
                    val();
            }
            
            if (typeof _selectFunc == "function") {
                _selectFunc.call(this,val,text,barbellType);
            }
            
            $div.
                modal("hide");
        });
        
        _dialogInit = true;
    }
    
    /**
     * Palauttaa objektin joka sisältää kategorian harjoitteet.
     *
     * @returns object
     */
    this.getCategoryExercises = function(category,musclegroup) {
        _init();
        
        var result = [];
        
        if (typeof category != "string" || typeof musclegroup != "string") {
            return result;
        }
        
        if (_exercises.hasOwnProperty(category)) {
            if (_exercises[category].hasOwnProperty(musclegroup)) {
                if (typeof _exercises[category][musclegroup] == "object") {
                    for (var key in _exercises[category][musclegroup]) {
                        result.push({
                            "_key":     key,
                            "_value":   _exercises[category][musclegroup][key]
                        });
                    }
                }
            }
        }
        
        // Järjesteä aakkosjärjestykseen
        result = result.sort(function(a,b) {
            if (a["_value"] < b["_value"]) {
                return -1;
            }
            if (a["_value"] > b["_value"]) {
                return 1;
            }
            return 0;
        });
        
        return result;
    }
    
    /**
     * Palauttaa kategorian ja lihasryhmän johon harjoite kuuluu.
     *
     * @param   int exerciseid  Harjoitteen id
     * @returns object
     */
    this.getCategoryForExercise = function(exerciseid) {
        _init();
        
        var result = false;
        
        loop:
        for (var cat in _exercises) {
            if (typeof _exercises[cat] == "object") {
                for (var group in _exercises[cat]) {
                    if (typeof _exercises[cat][group] == "object") {
                        for (var _exerciseid in _exercises[cat][group]) {
                            if (exerciseid == _exerciseid) {
                                result = {
                                    "category":     cat,
                                    "musclegroup":  group
                                }
                                break loop;
                            }
                        }
                    }
                }
            }
        }
        
        return result;
    }
    
    /**
     * Palauttaa objektin harjoitteiden kategorioista.
     *
     * @returns object
     */
    this.getExerciseCategories = function() {
        _init();
        
        var result = {};
        
        for (var cat in _exercises) {
            result[cat] = objGet(result,cat,{});
            if (typeof _exercises[cat] == "object") {
                for (var group in _exercises[cat]) {
                    result[cat][cat+"|"+group] = group;
                }
            }
        }
        
        return result;
    }
    
    /**
     * Palauttaa harjoituksen nimen.
     *
     * @param   int exerciseid  Harjoitteen id
     * @returns string
     */
    this.getExerciseText = function(exerciseid) {
        _init();
        
        var result = "";
        
        loop:
        for (var cat in _exercises) {
            if (typeof _exercises[cat] == "object") {
                for (var group in _exercises[cat]) {
                    if (typeof _exercises[cat][group] == "object") {
                        for (var _exerciseid in _exercises[cat][group]) {
                            if (exerciseid == _exerciseid) {
                                result = _exercises[cat][group][_exerciseid];
                                break loop;
                            }
                        }
                    }
                }
            }
        }
        
        return result;
    }
    
    /**
     * Funktio näyttää harjoitteen valitsemisen dialogin.
     *     
     * @param   int         exerciseid  Valittavan harjoitteen id (vapaaehtoinen)
     * @param   barbellType string      Valittavan harjoiteen levytankotyyppi (vapaaehtoinen)
     * @param   function    selectFunc  Funktio joka suoritetaan kun harjoitus valitaan (vapaaehtoinen)
     */
    this.showDialog = function(exerciseid,barbellType,selectFunc) {
        _initDialog();
        
        _selectFunc = selectFunc;
        
        if (typeof exerciseid != "undefined" && exerciseid) {
            var cat = ExerciseControl.
                getCategoryForExercise(exerciseid);
                
            $("#div-exercise-select-exercisecategory").
                val(cat["category"]+"|"+cat["musclegroup"]).
                trigger("change");
            
            $("#div-exercise-select-exerciseid").
                val(exerciseid);
        }
        
        if (typeof barbellType != "undefined") {
            $("#div-exercise-select-barbell-type").
                val(barbellType);
        }
        
        $("#div-exercise-select-dialog").
            modal("show");
    }
}

// Luokka satunnaisten lukujen generointia varten
var Rand = new function() {
    var _this = this;
    var _lastRandoms = [];
    
    /**
     * Generoi satunnaisen luvun joka kostuu kirjaimesta R, microtimestä ja
     * satunnaisesta luvusta väliltä 1 - 1 000 000.
     *
     * @returns string
     */
    this.generate = function() {
        var random = null;
        
        do {
            random = "R"+new Date().
                getTime().
                toString()+
                Math.
                    floor((Math.random()*1000000)+1).
                    toString(); 
        } while (_lastRandoms.indexOf(random) != -1);
        
        _lastRandoms.push(random);
        
        if (_lastRandoms.length >= 1100) {
            _lastRandoms = _lastRandoms.slice(0,1000);
        }
        
        return random;
    }
}

/**
 * Funktio lähettää kutsun rajapintaan.
 *
 * @param   string  className       Kutsuttava luokka
 * @param   string  functionName    Kutsuttava metodi
 * @param   object  data            Lähetettävä data (vapaaehtoinen)
 */
function apiCall(className,functionName,data,loader) {
    if (typeof data == "undefined" || data == null) {        
        data = {};
    }
    
    if (typeof loader == "undefined") {
        loader = true;
    }
    
    var _this = this;
    var $this = $(this);
    
    // Luodaan URL
    var url = "gateway/request"+
        "/"+
        className+
        "/"+
        functionName;
    
    // Latausruutu
    var $loader = $("#div-loader");
    
    // Avaa latausruutu
    if (loader) {
        showLoader();
    }
    
    __API_CALLS++;
    
    $.ajax({
        async:          true,
        cache:          false,
        contentType:    "application/json",
        data:           JSON.stringify(data),
        dataType:       "json",
        type:           "POST",
        url:            url,
        error:          function(xhr,status,error) {
            __API_CALLS--;
            
            // Sulje latausruutu
            if (loader) {
                hideLoader();
            }
            
            // Aktivoi painike
            if ($this.is(":button")) {
                $this.removeAttr("disabled");
            }
    
            alert("API call failed!");
        },
        success:        function(result,status,xhr) {
            __API_CALLS--;
            
            // Sulje latausruutu
            if (loader) {
                hideLoader();
            }
            
            // Aktivoi painike
            if ($this.is(":button")) {
                $this.removeAttr("disabled");
            }
            
            // Tarkasta löytyykö tälle käsittelijää
            var func = objGet(__APICALL_FUNC,className+"/"+functionName);
                
            // Kutsu funktiota
            if (typeof func == "function") {
                func.call(_this,result,status,xhr);
            }
        }
    });
}

/**
 * Funktio piiloittaa modaalisen vahvistusdialogin.
 */
function hideConfirm() {
    if (__CONFIRM_INIT) {
        $("#div-confirm").modal("hide");
    }
}

/**
 * Funktio piiloittaa modaalisen dialogin.
 */
function hideDialog() {
    if (__DIALOG_INIT) {
        $("#div-dialog").modal("hide");
    }
}

/**
 * Funktio piiloittaa modaalisen latausruudun.
 */
function hideLoader() {
    if (__LOADER_INIT) {
        $("#div-loader").modal("hide");
    }
}

/**
 * Funktio piiloittaa modaalisen syötedialogin.
 */
function hidePrompt() {
    if (__PROMPT_INIT) {
        $("#div-prompt").modal("hide");
    }
}

/**
 * Funktio lukee elementin sisällön JSON.parse funktion läpi ja tyhjentää
 * lopuksi elementin HTML:n.
 *
 * @param   string  id      Elementin id
 * @param   bool    empty   Tyhjennä elementin sisältö lopuksi (oletus TRUE)
 * @returns object
 */
function getElementJSON(id,empty) {
    var result = {};
    if (typeof empty == "undefined") {
        empty = true;
    }
    
    var $element = $(id);
    if ($element.length == 1) {
        try {
            var text = $("<div>").
                html(
                    $element.
                        text()
                ).
                text().
                trim();
            
            result = JSON.
                parse(text);
        
            if (empty) {
                $element.html("");
            }            
        } catch(e) {
            result = {};
        }        
    }
    
    return result;
}

/**
 * Funktio lukee elementin tekstisisällön ja tyhjentää lopuksi
 * elementin HTML:n.
 *
 * @param   string  id      Elementin id
 * @param   bool    empty   Tyhjennä elementin sisältö lopuksi (oletus TRUE)
 * @returns string
 */
function getElementText(id,empty) {
    var result = "";
    if (typeof empty == "undefined") {
        empty = true;
    }
    
    var $element = $(id);
    if ($element.length == 1) {
        result = $("<div>").
            html(
                $element.
                    text()
            ).
            text().
            trim();
        
        if (empty) {
            $element.html("");
        }
    }
    
    return result;
}

/**
 * Funktio lataa halutun alinäkymän elementtiin.
 *
 * @param   string      $to         Elementti johon ladataan
 * @param   string      view        Näkymän nimi
 * @param   string      subview     Alinäkymän nimi
 * @param   object      data        Lähettävä data (vapaaehtoinen)
 * @param   function    successFunc Funktio joka suoritetaan onnistuneessa kutsussa
 * @param   function    errorFunc   Funktio joka suoritetaan epäonnistuneessa kutsussa
 */
function getSubView($to,view,subview,data,successFunc,errorFunc) {
    if (typeof data != "object" || data == null) {
        data = {};
    }
    
    // Onko näkymä latautumassa
    var name = view+"/"+subview;
    if (objGet(__SUBVIEW_LOADING,name,false)) {
        return;
    }
    
    __SUBVIEW_LOADING[name] = true;
    
    var _this = this;
    
    showViewLoader($to);
    
    $.ajax({
        async:      true,
        cache:      false,
        data:       data,
        dataType:   "html",
        type:       "POST",
        url:        "view/subview.php?view="+escape(view)+"&subview="+escape(subview),
        error:      function(xhr,status,error) {  
            __SUBVIEW_LOADING[name] = false;            
            $to.text("Load failed!");
            
            if (typeof errorFunc == "function") {
                errorFunc.call(_this,xhr,status,error);
            }
        },
        success:    function(result,status,xhr) {
            __SUBVIEW_LOADING[name] = false;            
            $to.html(result);
            
            if (typeof successFunc == "function") {
                successFunc.call(_this,result,status,xhr);
            }
        }
    });
}

/**
 * Funktio lataa halutun näkymän ruudulle.
 *
 * @param   string  view    Näkymän nimi (oletus käyttäjän tilan mukaan)
 * @param   object  data    Lähettävä data (vapaaehtoinen)
 * @param   object  $to     Elementti johon ladataan (oletus #div-mainview)
 */
function getView(view,data,$to) {
    if (typeof view != "string") {
        view = "";
    }
    
    if (typeof data != "object" || data == null) {
        data = {};
    }
    
    if (typeof $to != "object") {
        $to = $("#div-mainview");
    }
            
    if (!__VIEW_LOADING && __API_CALLS == 0) {
        __VIEW_LOADING = true;
        
        hideDialog();
        showViewLoader($to);
        
        $.ajax({
            async:      true,
            cache:      false,
            data:       data,
            dataType:   "html",
            type:       "POST",
            url:        "view/view.php?view="+escape(view),
            error:      function(xhr,status,error) {
                __VIEW_LOADING = false;                
                $to.text("Load failed!");
            },
            success:    function(result,status,xhr) {
                __VIEW_LOADING = false;
                $to.html(result);
                
                // Aseta navigointipalkin valittu linkki aktiiviseksi
                var $navbar = $("#nav-navbar #navbar");
                if ($navbar.length) {
                    $navbar.
                        find("li.active").
                        removeClass("active");
                    
                    $navbar.
                        find("a[data-ui-view=\""+view+"\"]").
                        parents("li:first").
                        addClass("active");
                }
            }
        });
    }
}

/**
 * Lataa näkymään liittyvän JavaScript tiedoston ja suorittaa callback funktion.
 *
 * Jos JavaScript on jo kertalleen ladattu suoritetaan pelkkä callback.
 *
 * @param   string      view        Näkymän nimi
 * @param   function    callback    Latauksen jälkeen suoritettava funktio (vapaaehtoinen)
 */
function getViewScript(view,callback) {
    if (typeof view != "string" || view.length == 0) {
        return;
    }
    
    if ($.inArray(view,__VIEW_SCRIPTS) == -1) {
        $.getScript("view/"+view+"/"+view+".js",function(){            
            __VIEW_SCRIPTS.push(view);
            if (typeof callback == "function") {                
                callback.call(this);
            } 
        });
    } else if (typeof callback == "function") {
        callback.call(this);
    }
}

/**
 * Funktio tutkii onko objektissa haluttua attribuuttia ja palauttaa sen.
 *
 * Jos attribuuttia ei löydy palautetaan oletusarvo.
 *
 * @param   object  object      Taulukko
 * @param   mixed   key         Sarakkeen nimi
 * @param   mixed   default     Oletus (vapaaehtoinen, oletusarvo NULL)
 * @param   mixed   ignoreUndef Jos attribuutin arvo on undefined niin palauta oletus (vapaaehtoinen, oletus FALSE)
 * @returns mixed
 */
function objGet(object,key,defaultVal,ignoreUndef) {
    if (typeof defaultVal == "undefined") {
        defaultVal = null;
    }
    if (typeof ignoreUndef == "undefined") {
        ignoreUndef = false;
    }
    
    var result = defaultVal;    
    if (typeof object == "object" && object.hasOwnProperty(key)) {
        if (!ignoreUndef || typeof object[key] != "undefined") {
            result = object[key];
        }
    }    
    return result;
}

/**
 * Funktio näyttää modaalisen vahvistusdialogin.
 * 
 * @param   string      title       Dialogin otsikko
 * @param   function    confirmFunc Confirm painikkeen käsittelijä
 */
function showConfirm(title,confirmFunc) {
    var $confirm = $("#div-confirm");
    
    __CONFIRM_FUNC = confirmFunc;
    
    if (!__CONFIRM_INIT) {
        $confirm.show().modal({
            backdrop:   "static",
            keyboard:   false,
            show:       false
        });
        
        // Confirm painike
        $(document).on("click","#div-confirm-button-confirm",function(){
            hideConfirm();
            
            // Kutsu funktiota
            if (typeof __CONFIRM_FUNC == "function") {
                __CONFIRM_FUNC.call(this);
            }
        });
        
        __CONFIRM_INIT = true;
    }
    
    // Otsikko
    if (typeof title == "string" && title.trim().length) {
        $confirm.
            find("h4.modal-title").
            text(title.trim());
    } else {
        $confirm.
            find("h4.modal-title").
            html("&nbsp;");
    }
    
    $confirm.modal("show");
}

/**
 * Funktio näyttää modaalisen dialogin.
 *
 * Funktio palauttaa viittauksen dialogin bodyyn johon voi asettaa datan.
 *
 * @param   string  title   Dialogin otsikko
 * @returns object
 */
function showDialog(title) {
    var $dialog = $("#div-dialog");
    
    if (!__DIALOG_INIT) {
        $dialog.show().modal({
            backdrop:   "static",
            keyboard:   false,
            show:       false
        });
        __DIALOG_INIT = true;
    }
    
    // Otsikko
    if (typeof title == "string" && title.trim().length) {
        $dialog.
            find("h4.modal-title").
            text(title.trim());
    } else {
        $dialog.
            find("h4.modal-title").
            html("&nbsp;");
    }
    
    $dialog.modal("show");
    
    return $dialog.find("div.modal-body");
}

/**
 * Funktio näyttää modaalisen latatusruudun.
 */
function showLoader() {
    var $loader = $("#div-loader");
    
    if (!__LOADER_INIT) {
        $loader.show().modal({
            backdrop:   "static",
            keyboard:   false,
            show:       false
        });
        __LOADER_INIT = true;
    }
    
    $loader.modal("show");
}

/**
 * Funktio näyttää modaalisen syötedialogin.
 * 
 * @param   string      title       Dialogin otsikko
 * @param   string      text        Syötteen oletusteksti
 * @param   function    promptFunc  Syötteen vahvistuspainikkeen käsittelijä
 */
function showPrompt(title,text,promptFunc) {
    var $prompt     = $("#div-prompt");
    var $promptText = $("#div-prompt-input-text");
    
    __PROMPT_FUNC = promptFunc;
    
    if (!__PROMPT_INIT) {
        $prompt.show().modal({
            backdrop:   "static",
            keyboard:   false,
            show:       false
        });
        
        // Confirm painike
        $(document).on("click","#div-prompt-button-confirm",function(){
            hidePrompt();
            
            // Kutsu funktiota
            if (typeof __PROMPT_FUNC == "function") {
                __PROMPT_FUNC.call(this,$promptText.val());
            }
        });
        
        __PROMPT_INIT = true;
    }
    
    // Otsikko
    if (typeof title == "string" && title.trim().length) {
        $prompt.
            find("h4.modal-title").
            text(title.trim());
    } else {
        $prompt.
            find("h4.modal-title").
            html("&nbsp;");
    }
    
    // Oletusteksti
    if (typeof text == "string" && text.length) {
        $promptText.val(text);
    } else {
        $promptText.val("");
    }
    
    $prompt.modal("show");
}

/**
 * Funktio asettaa näkymään latausanimaation.
 *
 * @param   object  $to Elementti johon asetetaan
 */
function showViewLoader($to) {
    $to.
        empty().
        append($("<img>").attr("src","img/ajax-loader.gif"));    
}

/**
 * Funktio asettaa lähetettyyn lomakkeeseen virheilmoituksen tai onnistuneen
 * lähetyksen vastauksen.
 *
 * @param   object  data        Rajapinnan vastaus
 * @param   string  override    Ylikirjoittaa rajapinnan onnistuneen vastauksen    
 */
function setError(data,override) {
    if (typeof keyDesc != "object") {
        keyDesc = {};
    }
    
    var text = data.text;
    
    // Vastaus ok    
    if (data.code == 0) {
        // Ylikirjoita
        if (typeof override == "string" && override.length) {
            text = override;
        }
    
        var time    = 5000;        
        var div     = "alert-success";        
        var $text   = $("<div>").
            append(
                $("<div>").
                    text(text)
            );        
    } else { // Virhe                
        var time    = 10000;        
        var div     = "alert-danger";
        var $text   = $("<div>");
        
        // Hae iteraatiot
        var last = null;
        if (typeof data.iteration == "object" && typeof data.sourceDesc == "object") {
            for (var source in data.iteration) {
                if (data.sourceDesc.hasOwnProperty(source)) {
                    $text.
                        append(
                            $("<div>").
                                text(
                                    data.sourceDesc[source]+
                                    " ("+
                                    data.iteration[source]+
                                    ")"
                                )
                        );
                    last = source;
                }
            }
        }
        
        // Hae syyn lähteen kuvaus
        if (data.reasonSource != last && typeof data.sourceDesc == "object") {
            if (data.sourceDesc !== null && data.sourceDesc.hasOwnProperty(data.reasonSource)) {
                $text.
                    append(
                        $("<div>").
                            text(data.sourceDesc[data.reasonSource])
                    );
            }            
        }
        
        var $error = $("<div>").
            text(text);
        
        // Hae keyDesc
        if (typeof data.keyDesc == "string") {
            $error.text(data.keyDesc+" -> "+$error.text());
        }
        
        $error.
            text("* "+$error.text());
        
        $error.
            appendTo($text);
    }
    
    // Etsi lomake
    var $form = $(this).
        parents("form.form-submit:first");
    
    // Luo linkki
    $a = $("<a>").
        attr("name","a-error");
    
    // Luo div
    var $div = $("<div>").
        addClass("alert div-error "+div).
        attr("role","alert").
        append($text);
    
    $div.
        prepend($a);
    
    // Poista vanhat virheilmoitukset
    $form.
        find(".div-error").
        remove();
    
    // Lisää uusi virheilmoitus
    $div.
        appendTo($form);
    
    // Hyppaa linkkiin
    $("html,body").stop().animate({
        "scrollTop": $a.offset().top
    },0);
    
    // Aseta ajastin joka poistaa ilmoituksen
    setTimeout(function(){
        if ($div.length) {
            $div.
                fadeOut().
                remove();
        }
    },time);
}

// Lisätään jQueryyn serializeObject
$.fn.serializeObject = function(){
    var obj     = {};
    var array   = this.serializeArray();
    
    var iterateKeys = function(obj,keys,value) {
        if (keys.length > 0) {
            var key = keys[0];
            
            if (key.length == 0) {
                key = "0";
                while (obj.hasOwnProperty(key)) {
                    key = (parseInt(key)+1).toString();
                }
                obj[key] = {};                
            } else if (!obj.hasOwnProperty(key)) {
                obj[key] = {};                
            }
            
            keys = keys.slice(1);            
            if (keys.length > 0) {
                iterateKeys(obj[key],keys,value);
            } else {
                obj[key] = value;
            }
        }
    }
    
    $.each(array,function(){
        var keys    = [];
        var value   = this.value;
        
        var regExp = new RegExp("(\\[?([A-Za-z0-9_]{1,})\\]?)|(\\[\\])","g");
        var match = null;
        while (match = regExp.exec(this.name)) {
            var key = match[2];
            if (!key) {
                key = ""
            }
            keys.push(key);
        }
        
        iterateKeys(obj,keys,value);
    });
    
    return obj;
};

// Järjestelmän sisäiset linkit
$(document).on("click",".a-navigation,.button-navigation",function(){
    var $this   = $(this);
    var data    = $this.data();
    var action  = objGet(data,"uiAction","",true);
    var view    = objGet(data,"uiView","",true);

    var obj = {};
    for (var key in data) {
        if (key != "uiView" && key != "uiAction") {
            obj[key] = data[key];
        }
    }

    if (action) {
        switch (action) {
            case "logout":
                apiCall.call(this,"Login","doLogout",obj);
                break;
        }        
    }
    
    if (view) {
        getView(view,obj);
    }
});

// Avaa dialogiin alinäkymän
$(document).on("click",".a-subview-dialog,.button-subview-dialog",function(){
    var $this   = $(this);
    var data    = $this.data();    
    var view    = objGet(data,"uiView","",true);
    var subview = objGet(data,"uiSubview","",true);
    var title   = objGet(data,"uiTitle","",true);

    var obj = {};
    for (var key in data) {
        if (key != "uiView" && key != "uiSubview" && key != "uiTitle") {
            obj[key] = data[key];
        }
    }

    if (view && subview) {
        var $body = showDialog(title);
        
        getSubView($body,view,subview,obj);       
    }
});

// Submit painikkeen käsitelijä
$(document).on("click",".button-submitform",function(){
    var $this = $(this);
    var $form           = $this.parents("form.form-submit:first");
    var className       = $this.data("classname");
    var functionName    = $this.data("functionname");
    var data            = $form.serializeObject();
        
    // Poista vanhat virheilmoitukset
    $form.find(".div-error").remove();
    
    // Passivoi painike
    $this.attr("disabled","disabled");
        
    // Kutsu rajapintaa
    apiCall.call(this,className,functionName,data);
});

$(document).ready(function(){
    // Avaa oletussivu
    getView();
});